package com.gs.process.core.configuration;

import com.alibaba.fastjson.JSON;
import com.gs.process.core.ApprovalApi;
import com.gs.process.core.ApprovalDetailsApi;
import com.gs.process.core.annotation.ProcessBegin;
import com.gs.process.core.handler.ProcessHandlerHolder;
import com.gs.process.core.handler.ProcessHandlerService;
import com.gs.process.core.interceptor.ProcessConfigurer;
import com.gs.process.core.interceptor.ProcessContextManager;
import com.gs.process.core.interceptor.WebInterceptor;
import com.gs.process.core.server.HttpHandler;
import com.gs.process.core.server.NettyServer;
import com.gs.process.core.user.ProcessUserHolder;
import com.gs.process.core.user.ProcessUserService;
import com.gs.process.core.utils.AspectUtil;
import com.gs.process.core.utils.HttpTokenUtil;
import com.shallowUniverse.http.HttpApiService;
import com.shallowUniverse.http.pojo.HttpResult;
import com.shallowUniverse.model.Result;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: Shuai Guo
 * @description:
 * @date: 2021/9/27
 * @version: 1.0
 */
@Slf4j
@Aspect
@Configuration
@EnableConfigurationProperties(ProcessConfig.class)
@ConditionalOnBean(value = {HttpApiService.class, DataSourceAutoConfiguration.class})
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class ProcessApiAutoConfiguration implements WebMvcConfigurer {

    @Resource
    private HttpApiService httpApiService;
    @Resource
    private ProcessConfig processConfig;
    @Resource
    private ApplicationContext context;
    @Resource
    private DataSource dataSource;

    @Bean
    public ApprovalApi approvalApi() {
        ApprovalApi approvalApi = new ApprovalApi();
        approvalApi.setHttpApiService(httpApiService);
        approvalApi.setProcessConfig(processConfig);
        approvalApi.setProcessUserService(processUserService());
        approvalApi.setDataSource(dataSource);
        return approvalApi;
    }

    @Bean
    public ApprovalDetailsApi approvalDetailsApi() {
        ApprovalDetailsApi approvalDetailsApi = new ApprovalDetailsApi();
        approvalDetailsApi.setHttpApiService(httpApiService);
        approvalDetailsApi.setProcessConfig(processConfig);
        approvalDetailsApi.setProcessUserService(processUserService());
        return approvalDetailsApi;
    }

    @Bean
    public ProcessContextManager processContextManager() {
        ProcessContextManager processContextManager = new ProcessContextManager(processUserService());
        Map<String, ProcessConfigurer> configurers = context.getBeansOfType(ProcessConfigurer.class);
        if (!CollectionUtils.isEmpty(configurers)) {
            configurers.forEach((key, value) -> {
                value.configurer(processContextManager);
            });
        }
        //设置工具类用户管理器
        HttpTokenUtil.setProcessUserService(processUserService());
        //判断是否是管理程序
        if("client".equals(processConfig.getType())){
            //推送权限和配置信息
            new ProcessConfigPusher().pushToServer();
            new Thread(() -> {
                //启动netty服务
                try {
                    new NettyServer(9001, new HttpHandler(processHandlerService(), new QueryRunner(dataSource))).start();
                } catch (Exception e) {
                    e.printStackTrace();
                    log.debug("监听服务器启动失败.............");
                }
            }).start();
        }
        //返回流程上下文管理器
        return processContextManager;
    }

    @Bean
    public ProcessHandlerService processHandlerService() {
        return new ProcessHandlerHolder(context, processConfig.getHandlerPackages());
    }

    @Bean
    public ProcessUserService processUserService() {
        return new ProcessUserHolder();
    }

    @SneakyThrows
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new WebInterceptor(processContextManager(), processUserService()));
    }

    @Around("@annotation(processBegin)")
    public Object around(ProceedingJoinPoint joinPoint, ProcessBegin processBegin) {
        return AspectUtil.beginProcess(approvalApi(), joinPoint, processBegin);
    }

    private class ProcessConfigPusher {
        private final Logger logger = LoggerFactory.getLogger(ProcessConfigPusher.class);

        public void pushToServer() {
            //发送推送请求
            try {
                HttpResult httpResult = httpApiService.doPost(processConfig.getServerUrl() + "/receive/config", null, processConfig);
                Result<?> requestResult = JSON.parseObject(httpResult.getBody(), Result.class);
                //判断是否成功
                if (requestResult.getCode().equals(200)) {
                    logger.info("配置信息推送成功.............");

                    new UserRolePusher().pushToServer(dataSource);
                } else {
                    logger.info("配置信息推送失败.............");
                }
            } catch (IOException | SQLException e) {
                e.printStackTrace();
                logger.info("配置信息推送异常................");
            }
        }
    }

    public class UserRolePusher {

        private final Logger logger = LoggerFactory.getLogger(UserRolePusher.class);

        public void pushToServer(DataSource dataSource) throws SQLException {
            //判断是否配置了业务库
            if (ObjectUtils.isEmpty(processConfig.getBusinessDatabase())) {
                return;
            }
            //定义推送结果
            Map<String, List<Map<String, Object>>> result = new HashMap<>();
            QueryRunner runner = new QueryRunner(dataSource);
            //开始查询用户角色部门信息
            result.put("userList", commonQuery(runner, processConfig.getUserTable(), processConfig.getUserId()));
            //开始查询角色信息
            result.put("roleList", commonQuery(runner, processConfig.getRoleTable(), processConfig.getRoleId()));
            //开始查询部门信息
            result.put("deptList", commonQuery(runner, processConfig.getDeptTable(), processConfig.getDeptId()));
            //发送推送请求
            try {
                HttpResult httpResult = httpApiService.doPost(processConfig.getServerUrl() + "/receive/users", null, result);
                Result<?> requestResult = JSON.parseObject(httpResult.getBody(), Result.class);
                //判断是否成功
                if (requestResult.getCode().equals(200)) {
                    logger.info("权限信息推送成功.............");
                } else {
                    logger.info("权限信息推送失败.............");
                }
            } catch (IOException e) {
                e.printStackTrace();
                logger.info("权限信息推送异常................");
            }
        }

        private List<Map<String, Object>> commonQuery(QueryRunner runner, String table, String id) throws SQLException {
            if (!ObjectUtils.isEmpty(table) && !ObjectUtils.isEmpty(id)) {
                //定义sql
                String user_sql = String.format("select * from %s.%s", processConfig.getBusinessDatabase(), table);
                return runner.query(user_sql, new MapListHandler());
            }
            return new ArrayList<>();
        }
    }
}
